
#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>

#include <unistd.h>

extern "C"
{
    #include "lua.h"
    #include "lualib.h"
    #include "lauxlib.h"
};

#include "lua_tinker.h"

using namespace std ;


class Player
{
public:
    Player():m_money(0) { printf("Player\n") ; } ;
    ~Player() { printf("~Player\n") ; } ;

    int get_money() const { return m_money ; }
    int add_money(int delt_value)
    {
        if(delt_value < 1 || m_money + delt_value < 1 ) return -1 ;
        m_money += delt_value ;
        return 0 ;
    }

    int sub_money(int delt_value)
    {
        if(delt_value < 1 || m_money - delt_value < 1 ) return -1 ;
        m_money -= delt_value ;
        return 0 ;
    }

    int get_item_count(int item_id) const
    {
        auto it = m_item_list.find(item_id) ;
        if ( it == m_item_list.end() ) return 0 ;
        return it->second ;
    }

    int add_item(int item_id,int delt_value)
    {
        int item_count = get_item_count(item_id) ;
        if(delt_value < 1 || item_count + delt_value < 1 ) return -1 ;
        m_item_list[item_id] += delt_value ;
        return 0 ;
    }

    int sub_item(int item_id,int delt_value)
    {
        int item_count = get_item_count(item_id) ;
        if(delt_value < 1 || item_count - delt_value < 1 ) return -1 ;
        m_item_list[item_id] -= delt_value ;
        return 0 ;

    }

    void output()
    {
        printf("money:%d\n",m_money) ;
        for(auto const& item : m_item_list)
        {
            printf("item_id:%d item_count:%d\n",item.first,item.second) ;
        }
    }

private:
    int m_money ;
    unordered_map<int,int> m_item_list ;

} ;

void log_info(const char* content)
{
    printf("[info]%s\n",content);
}

class PluginManager
{
public:
    typedef unordered_set<string> CallbackContainer ;
    typedef unordered_map<string,CallbackContainer > HandlerContainer ;
public:
    PluginManager():m_L(nullptr) { printf("PluginManager\n");} ;

    ~PluginManager()
    {
        if(m_L) lua_close(m_L);
        printf("~PluginManager\n") ;
    }
    
    void register_handler(const char* event,const char* handler_name) 
    {
        CallbackContainer& cc = m_handler[event] ;
        //if(cc.size() < 1 ) cc.reserve(8) ;
        cc.insert(handler_name) ;
    }

    void run(Player* player) 
    {
        for(auto const& it : m_handler)
        {
            const CallbackContainer& cc = it.second ;
            for(auto const& func_it : cc)
            {
                printf("run event:%s func_name:%s\n",it.first.c_str(),func_it.c_str()) ;
                lua_tinker::call<int>(m_L,func_it.c_str(),player ) ;
            }
        }
    }

    int init() ;
    void export_manager(lua_State* lua) ;    

    int load(const char* lua_file)
    {
        if(nullptr == m_L) return -1 ;
        lua_tinker::dofile(m_L,lua_file) ;
        return 0 ;
    }


private:
    lua_State* m_L ;
    HandlerContainer m_handler ;

} ;

void PluginManager::export_manager(lua_State* m_L)
{
        lua_tinker::class_add<PluginManager>(m_L, "PluginManager");
        lua_tinker::class_def<PluginManager>(m_L, "register_handler", &PluginManager::register_handler);
        lua_tinker::set<PluginManager*>(m_L, "plugin_manager", this );
        lua_tinker::def(m_L, "log_info", log_info);
}

void export_player(lua_State* m_L)
{
        lua_tinker::class_add<Player>(m_L, "Player");
        lua_tinker::class_def<Player>(m_L, "get_money", &Player::get_money);
        lua_tinker::class_def<Player>(m_L, "add_money", &Player::add_money);
        lua_tinker::class_def<Player>(m_L, "sub_money", &Player::sub_money);
        lua_tinker::class_def<Player>(m_L, "get_item_count", &Player::get_item_count);
        lua_tinker::class_def<Player>(m_L, "add_item", &Player::add_item);
        lua_tinker::class_def<Player>(m_L, "sub_item", &Player::sub_item);
}

    int PluginManager::init()
    {
        m_L = lua_open();
        if(nullptr == m_L) return -1 ;
        luaopen_base(m_L);
        luaopen_string(m_L);
        luaopen_os(m_L) ;
        export_manager(m_L) ;
        export_player(m_L) ;
        return 0 ;
    }
 

int main(int argc,char** argv)
{
#if 0
    PluginManager manager ;
    if( manager.init() !=0 ) 
    {
        printf("init failed\n") ;
        return -1 ;
    }

    manager.load("plugin_login.lua") ;
    manager.load("plugin_logout.lua") ;
    manager.load("plugin_login_reload.lua") ;

    manager.run() ;
#else 

    const char* plugin_list[] = { 
        "plugin_login.lua",
        "plugin_login_reload.lua" ,
        "plugin_logout.lua",
    } ;

    int plugin_size = sizeof(plugin_list)/sizeof(const char*) ;
    PluginManager* managers = new PluginManager[plugin_size];
    for(int i = 0 ; i < plugin_size ; ++i)
    {   
        if(managers[i].init()!=0) 
        {
            printf("init %d failed\n",i) ;
            return -1 ;
        }

        managers[i].load(plugin_list[i]) ;
    
    }

    Player player ;
    
    for(int count=0;count < 100 ; ++count)
    {

    for(int i = 0 ; i < plugin_size ; ++i)
    {
        managers[i].load(plugin_list[i]) ;
        managers[i].run(&player) ;
    }

    player.output();

    sleep(10) ;
    }

#endif
    return 0 ;
}
